#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Project    : flask-tutorial
# @Author     : wangc
# @CreateTime : 2019/4/9 10:25
# @File       : auth

import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash

from .db import get_db

# 创建一个名称为 'auth' 的 Blueprint 。
# url_prefix 会添加到所有与该蓝图关联的 URL 前面。
bp = Blueprint('auth', __name__, url_prefix='/auth')


@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method == 'POST':
        # request.form 是一个特殊类型的 dict ，其映射了提交表单的键和值。
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None

        # 验证 username 和 password 不为空。
        if not username:
            error = '用户名必输'
        elif not password:
            error = '密码必输'
        elif db.execute(
            # 使用占位符?，自动转义输入值，以抵御 SQL 注入攻击 。
            "SELECT id FROM user WHERE username = ?", (username,)
        ).fetchone() is not None:
            error = '用户{}已注册'.format(username)

        if error is None:
            # 使用 generate_password_hash() 生成安全的哈希值并储存 到数据库中
            db.execute(
                "INSERT INTO user(username, password) VALUES(?, ?)",
                (username, generate_password_hash(password))
            )
            db.commit()
            #  url_for() 根据登录视图的名称生成相应的 URL 。
            return redirect(url_for('auth.login'))

        # flash() 用于储存在渲染模块时可以调用的信息。
        flash(error)

    return render_template('auth/register.html')


@bp.route('login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None

        user = db.execute(
            'SELECT id, password FROM user WHERE username = ?', (username,)
        ).fetchone()

        if user is None:
            error = '用户名有误'
        # check_password_hash() 以相同的方式哈希提交的 密码并安全的比较哈希值。
        elif not check_password_hash(user['password'], password):
            error = '密码有误'

        if error is None:
            # session 是一个 dict ，它用于储存横跨请求的值。
            # 会话数据被储存到一个 向浏览器发送的 cookie 中，在后继请求中，浏览器会返回它。
            # Flask 会安全对数据进行 签名 以防数据被篡改。
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))

        flash(error)

    return render_template('auth/login.html')


# bp.before_app_request() 注册一个 在视图函数之前运行的函数
@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')

    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()


@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))


def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            # login 函数 的端点为 'auth.login'，其中'auth'为auth蓝图
            return redirect(url_for('auth.login'))

        return view(**kwargs)

    return wrapped_view
